﻿using System;

namespace MSPToolkit.Compression
{
    internal class PendingBuffer
    {
        protected byte[] buf;
        int start;
        int end;

        uint bits;
        int bitCount;

        public PendingBuffer()
            : this(4096)
        {

        }

        public PendingBuffer(int bufsize)
        {
            buf = new byte[bufsize];
        }

        public void Reset()
        {
            start = end = bitCount = 0;
        }

        public void WriteShort(int s)
        {
            if (DeflaterConstants.DEBUGGING && start != 0)
            {
                throw new Exception();
            }
            buf[end++] = (byte)s;
            buf[end++] = (byte)(s >> 8);
        }

        public void WriteInt(int s)
        {
            if (DeflaterConstants.DEBUGGING && start != 0)
            {
                throw new Exception();
            }
            buf[end++] = (byte)s;
            buf[end++] = (byte)(s >> 8);
            buf[end++] = (byte)(s >> 16);
            buf[end++] = (byte)(s >> 24);
        }

        public void WriteBlock(byte[] block, int offset, int len)
        {
            if (DeflaterConstants.DEBUGGING && start != 0)
            {
                throw new Exception();
            }
            System.Array.Copy(block, offset, buf, end, len);
            end += len;
        }

        public int BitCount
        {
            get
            {
                return bitCount;
            }
        }

        public void AlignToByte()
        {
            if (DeflaterConstants.DEBUGGING && start != 0)
            {
                throw new Exception();
            }
            if (bitCount > 0)
            {
                buf[end++] = (byte)bits;
                if (bitCount > 8)
                {
                    buf[end++] = (byte)(bits >> 8);
                }
            }
            bits = 0;
            bitCount = 0;
        }

        public void WriteBits(int b, int count)
        {
            if (DeflaterConstants.DEBUGGING && start != 0)
            {
                throw new Exception();
            }
            bits |= (uint)(b << bitCount);
            bitCount += count;
            if (bitCount >= 16)
            {
                buf[end++] = (byte)bits;
                buf[end++] = (byte)(bits >> 8);
                bits >>= 16;
                bitCount -= 16;
            }
        }

        public void WriteShortMSB(int s)
        {
            if (DeflaterConstants.DEBUGGING && start != 0)
            {
                throw new Exception();
            }
            buf[end++] = (byte)(s >> 8);
            buf[end++] = (byte)s;
        }

        public bool IsFlushed
        {
            get
            {
                return end == 0;
            }
        }

        public int Flush(byte[] output, int offset, int length)
        {
            if (bitCount >= 8)
            {
                buf[end++] = (byte)bits;
                bits >>= 8;
                bitCount -= 8;
            }
            if (length > end - start)
            {
                length = end - start;
                System.Array.Copy(buf, start, output, offset, length);
                start = 0;
                end = 0;
            }
            else
            {
                System.Array.Copy(buf, start, output, offset, length);
                start += length;
            }
            return length;
        }

        public byte[] ToByteArray()
        {
            byte[] ret = new byte[end - start];
            System.Array.Copy(buf, start, ret, 0, ret.Length);
            start = 0;
            end = 0;
            return ret;
        }
    }
}
